/*
 * Copyright 2012 Faculty of Informatics - Masaryk University.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cz.muni.fi.pa165.creatures.web;

import cz.muni.fi.pa165.creatures.dao.exceptions.WeaponEffectivityException;
import cz.muni.fi.pa165.creatures.dto.CreatureTypeDTO;
import cz.muni.fi.pa165.creatures.dto.CreatureTypeWeaponDTO;
import cz.muni.fi.pa165.creatures.dto.WeaponDTO;
import cz.muni.fi.pa165.creatures.services.CreatureTypeService;
import cz.muni.fi.pa165.creatures.services.EffectivityService;
import cz.muni.fi.pa165.creatures.services.WeaponService;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.RedirectResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.integration.spring.SpringBean;
import net.sourceforge.stripes.security.action.Secure;
import net.sourceforge.stripes.validation.FloatTypeConverter;
import net.sourceforge.stripes.validation.IntegerTypeConverter;
import net.sourceforge.stripes.validation.LocalizableError;
import net.sourceforge.stripes.validation.Validate;
import net.sourceforge.stripes.validation.ValidateNestedProperties;
import net.sourceforge.stripes.validation.ValidationErrors;
import net.sourceforge.stripes.validation.ValidationMethod;

/**
 *
 * @author Ondrej Vojtisek
 */
@UrlBinding("/types/{$event}/{creatureTypeDTO.id}")
public class CreatureTypeActionBean extends CreatureHuntingActionBean {

    final static Logger logger =
            Logger.getLogger(CreatureTypeActionBean.class.getName());

    private List<String> selected;

    @SpringBean
    private CreatureTypeService creatureTypeService;

    @SpringBean
    private WeaponService weaponService;

    @SpringBean
    private EffectivityService effectivityService;


    @ValidateNestedProperties(value = {
        @Validate(on = {"add"},
                  field = "name",
                  required = true,
                  minlength = 2,
                  maxlength = 256),
        @Validate(converter = IntegerTypeConverter.class,
                  on = {"add"},
                  field = "maxHitPoints",
                  required = true,
                  minvalue = 1,
                  maxvalue = 1000),
        @Validate(on = {"add"},
                  field = "creaturesInGroup",
                  required = true,
                  converter = IntegerTypeConverter.class,
                  minvalue = 1,
                  maxvalue = 1000),
        @Validate(on = {"add"},
                  field = "description",
                  required = true,
                  minlength = 3,
                  maxlength = 256)
    })
    private CreatureTypeDTO creatureTypeDTO;

    private CreatureTypeWeaponDTO creatureTypeWeaponDTO;

    private Float efficiency;

    private WeaponDTO weaponDTO;

    public CreatureTypeWeaponDTO getCreatureTypeWeaponDTO() {
        return creatureTypeWeaponDTO;
    }

    public void setCreatureTypeWeaponDTO(CreatureTypeWeaponDTO creatureTypeWeaponDTO) {
        this.creatureTypeWeaponDTO = creatureTypeWeaponDTO;
    }

    public WeaponDTO getWeaponDTO() {
        return weaponDTO;
    }

    public void setWeaponDTO(WeaponDTO weaponDTO) {
        this.weaponDTO = weaponDTO;
    }

    public Float getEfficiency() {
        return efficiency;
    }

    public void setEfficiency(Float efficiency) {
        this.efficiency = efficiency;
    }

    public List<WeaponDTO> getWeapons() {
        return weaponService.getAllNotAsignedByCreatureType(creatureTypeDTO);
    }

    public List<CreatureTypeWeaponDTO> getEffectivity(){
        return effectivityService.getAllByCreatureType(Long.valueOf(creatureTypeDTO.getId()));
    }

    public List<String> getSelected() {
        return selected;
    }

    public void setSelected(List<String> selected) {
        this.selected = selected;
    }

    public CreatureTypeDTO getCreatureTypeDTO() {
        return creatureTypeDTO;
    }

    public void setCreatureTypeDTO(CreatureTypeDTO creatureTypeDTO) {
        this.creatureTypeDTO = creatureTypeDTO;
    }

    public Long getCount() {
        logger.log(Level.INFO, "count()");
        return creatureTypeService.getCount();
    }

    public Integer getNumOfAssociatedWeapons(){
         logger.log(Level.INFO, "getNumOfAssociatedWeapons()");
         return getEffectivity().size();
    }

    @Secure(roles = "admin")
    public Resolution create() {
        logger.log(Level.INFO, "create()");
        return new ForwardResolution("/types_create.jsp");
    }

    @Secure(roles = "admin")
    @HandlesEvent("add")
    public Resolution add() {
        if (creatureTypeDTO == null || creatureTypeDTO.getName() == null) {
            ValidationErrors errors = new ValidationErrors();
            errors.add("name", new LocalizableError("creatureType.name_not_set"));
            getContext().setValidationErrors(errors);
            return getContext().getSourcePageResolution();
        }
        logger.log(Level.INFO, "add({0})", creatureTypeDTO);
        creatureTypeService.create(creatureTypeDTO);
        return new RedirectResolution(this.getClass(), "all");
    }

    @DefaultHandler
    public Resolution all() {
        logger.log(Level.INFO, "all()");
        return new ForwardResolution("/types.jsp");
    }
    
    public List<CreatureTypeDTO> getAll() {
        logger.log(Level.INFO, "getAll()");
        return creatureTypeService.getAll();
    }

    @Secure(roles = "admin")
    public Resolution edit() {
        logger.log(Level.INFO, "edit()");
        creatureTypeDTO = creatureTypeService.getById(
                Long.parseLong(creatureTypeDTO.getId()));
        return new ForwardResolution("/types_edit.jsp");
    }

    public Resolution detail() {
        logger.log(Level.INFO, "detail()");
        creatureTypeDTO = creatureTypeService.getById(
                Long.parseLong(creatureTypeDTO.getId()));
        return new ForwardResolution("/types_detail.jsp");
    }

    @Secure(roles = "admin")
    public Resolution save() {
        logger.log(Level.INFO, "save()");
        CreatureTypeDTO ctDTO = creatureTypeService.getById(Long.parseLong(creatureTypeDTO.getId()));
        if (ctDTO.getMaxHitPoints() != null) {
            if (creatureTypeDTO.getMaxHitPoints() < ctDTO.getMaxHitPoints()) {
                creatureTypeDTO.setMaxHitPoints(ctDTO.getMaxHitPoints());
            }
        }
        creatureTypeService.update(creatureTypeDTO);
        return new RedirectResolution(this.getClass(), "all");
    }

    @Secure(roles = "admin")
    public Resolution remove() {
        logger.log(Level.INFO, "remove()");
        CreatureTypeDTO creatureTypeDB =
                creatureTypeService.getById(
                Long.parseLong(creatureTypeDTO.getId()));
        creatureTypeService.remove(creatureTypeDB);
        return new RedirectResolution(this.getClass(), "all");
    }
    
    @Secure(roles = "admin")
    public Resolution deleteSelected() {
        logger.log(Level.INFO, "deleteSelected()");
        if (selected != null) {
            for (String id : selected) {
                CreatureTypeDTO creatureTypeDB = creatureTypeService.getById(Long.parseLong(id));
                if (creatureTypeDB != null) {
                    creatureTypeService.remove(creatureTypeDB);
                }
            }
        }
        return new RedirectResolution(this.getClass(), "all");
    }
    @HandlesEvent("assignWeapon")
    @Secure(roles = "admin")
    public Resolution assignWeapon() {
         logger.log(Level.INFO, "assignWeapon()");
         ValidationErrors errors = new ValidationErrors();
         if(efficiency == null || weaponDTO == null){
            errors.add("name", new LocalizableError("creatureType.efficiency_not_set"));
            getContext().setValidationErrors(errors);
            creatureTypeDTO = creatureTypeService.getById(
            Long.parseLong(creatureTypeDTO.getId()));
            return new ForwardResolution("/types_edit.jsp");
        }
           if(efficiency < 1 || efficiency > 100){
            errors.add("name", new LocalizableError("creatureType.efficiency_out_of_bounds"));
            getContext().setValidationErrors(errors);
            creatureTypeDTO = creatureTypeService.getById(
            Long.parseLong(creatureTypeDTO.getId()));
            return new ForwardResolution("/types_edit.jsp");
        }
         
        WeaponDTO weaponDB = weaponService.getById(Long.parseLong(weaponDTO.getId()));
        CreatureTypeDTO creatureTypeDB =
                creatureTypeService.getById(Long.parseLong(creatureTypeDTO.getId()));
        try {
            effectivityService.addEffectivity(creatureTypeDB, weaponDB, efficiency);
        } catch (WeaponEffectivityException ex) {
             return new RedirectResolution(this.getClass(), "all");
        }
        return new RedirectResolution(this.getClass(), "all");
    }

    String weaponId;

    public void setWeaponId(String weaponId) {
        this.weaponId = weaponId;
    }

    public String getWeaponId() {
        return weaponId;
    }

    String creatureTypeId;

    public String getCreatureTypeId() {
        return creatureTypeId;
    }

    public void setCreatureTypeId(String creatureTypeId) {
        this.creatureTypeId = creatureTypeId;
    }

    @Secure(roles = "admin")
    public Resolution deleteWeapon() {
        logger.log(Level.INFO, "deleteWeapon()");
        effectivityService.removeEffectivity(
                Long.parseLong(creatureTypeId),
                Long.parseLong(weaponId));
        return new RedirectResolution(this.getClass(), "all");
    }

    @Secure(roles = "admin")
    public Resolution deleteAll() {
        weaponService.removeAll();
        logger.log(Level.INFO, "deleteAll()");
        return new RedirectResolution(this.getClass(), "all");
    }

    @ValidationMethod(on = {"add"})
    public void validateUniqueAddWeaponName() {
        ValidationErrors errors = getContext().getValidationErrors();
        if (creatureTypeService.getByName(creatureTypeDTO.getName()) != null) {
            errors.add("creatureTypeDTO.name",
                    new LocalizableError("creatureTypeDTO.already_exists"));
        }
    }

    @ValidationMethod(on = {"save"})
    public void validateUniqueSaveRegionName() {
        ValidationErrors errors = getContext().getValidationErrors();
        CreatureTypeDTO found = creatureTypeService.getByName(creatureTypeDTO.getName());
        if (found != null && !found.getId().equals(creatureTypeDTO.getId())) {
            errors.add("creatureTypeDTO.name",
                    new LocalizableError("creatureTypeDTO.already_exists"));
        }
    }

}
